﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;
using System.Text.RegularExpressions;



namespace PAEPing4
{
    public partial class FormPAEPing : Form
    {
        StringBuilder results = new StringBuilder();

        static bool stop;
        static readonly object stopLock = new object();

        public static bool Stop
        {
            get { lock (stopLock) { return stop; } }
            set { lock (stopLock) { stop = value; } }
        }

        private long _min;
        public long min
        {
            get { return _min; }
            set { _min = value; }
        }

        private long _max;
        public long max
        {
            get { return _max; }
            set { _max = value; }
        }

        private long _avg;
        public long avg
        {
            get { return _avg; }
            set { _avg = value; }
        }

        public TextBox textboxHost
        {
            get { return textBoxHost; }
            //no setter needed.
        }

        public TextBox textboxPacketSize
        {
            get { return textBoxPacketSize; }
            // no setter needed.
        }

        private bool _DNSLookup = false;
        public bool DNSLookup
        {
            get { return _DNSLookup; }
            set { _DNSLookup = value; }
        }

        private string _hn = "";
        public string hn
        {
            get { return _hn; }
            set { _hn = value; }
        }

        int count = 0;
        int sent = 0;
        int good = 0;
        int bad = 0;
        int packetSize = 32;
        string target;
        

        public void reset()
        {
            min = 0;
            max = 0;
            avg = 0;
            count = 0;
            sent = 0;
            good = 0;
            bad = 0;
            textBoxResults.Clear();
            results.Remove(0, results.Length);
        }

        public FormPAEPing()
        {
            InitializeComponent();
        }

        private void textBoxHost_TextChanged(object sender, EventArgs e)
        {
            return;
        }

        private void checkKeys(object sender, System.Windows.Forms.KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)13)
            {
                Stop = false;
                run();
            }
        }

        private void buttonPing_Click(object sender, EventArgs e)
        {
            Stop = false;
            run();
        }

        private void run()
        {
            min = 0;
            max = 0;
            avg = 0;
            count = 0;
            sent = 0;
            good = 0;
            bad = 0;
            target = textboxHost.Text;

            try
            { 
                packetSize = Convert.ToInt32(textboxPacketSize.Text);
                }
                catch (Exception ex) 
                { 
                    tellError(ex);
                    packetSize = 32; 
                }
                
            if (!checkBoxTrace.Checked)
            {
                results.AppendLine(String.Format("PAEPing Started at {0} {2}{2}Pinging {1} with 32 bytes of data. {2}",
                                    DateTime.Now, target, Environment.NewLine));
                updateResults();
            }
            else if (checkBoxTrace.Checked)
            {
                results.AppendLine(String.Format("PAEPing Started at {0} {2}{2}Tracing Route to host: {1} over a maximum of 30 hops. {2}",
                                    DateTime.Now, target, Environment.NewLine));
                updateResults();
            }

            new Thread(new ThreadStart(doPing)).Start();
        }

        private void doPing()
        {
            Ping ping = new Ping();
            
            #region Continuous Ping
		    if (checkboxContinuous.Checked)
            {
                while (!Stop)
                {
                    if (!Stop)
                    {
                        try
                        {
                            PingReply reply = ping.Send(target, 1000, new byte[packetSize]);
                            count++;
                            sent++;
                            try
                            {
                                if (reply.Address != null) 
                                { 
                                    hn = getHostName(reply.Address.ToString()); 
                                }

                            }
                            catch (Exception ex)
                            {
                                hn = reply.Address.ToString();
                                tellError(ex);
                            }
                            tellUI(reply, hn); 
                            Thread.Sleep(250);
                        }
                        catch (Exception ex)
                        {
                            tellError(ex);
                            return;
                        }
                    }
                }
                complete();
            }
 	        #endregion            
 
            #region Trace
            else if (checkBoxTrace.Checked)
            {
                for (int i = 1; i < 32; i++)
                {
                    if (!Stop)
                    {
                        try
                        {
                            PingReply reply = ping.Send(target, 500, new byte[32], new PingOptions(i, true));
                            count++;

                            string[] tracetimes = new string[3];
                            string tracehost = "NO REPLY";
                            if (reply.Address != null)
                            {
                                tracehost = reply.Address.ToString();
                            }
                            bool tracerror = false;
                            for (int j = 1; j <= 3; j++)
                            {
                                try
                                {
                                    if (reply.Address != null)
                                    {
                                        PingReply trace = ping.Send(reply.Address);
                                        if (trace.Status == IPStatus.Success)
                                        {
                                            tracetimes[j - 1] = trace.RoundtripTime.ToString();
                                        }
                                        else
                                        {
                                            tracetimes[j - 1] = "*";
                                            tracerror = true;
                                        }
                                    }
                                    else
                                    {
                                        tracetimes[0] = "*";
                                        tracetimes[1] = "*";
                                        tracetimes[2] = "*";
                                        tracerror = true;
                                    }
                                }
                                catch (Exception ex)
                                {
                                    tellError(ex);
                                    tracerror = true;
                                }
                                finally
                                {
                                    sent++;
                                }
                            }
                            traceResult(tracetimes[0], tracetimes[1], tracetimes[2], tracehost, tracerror);
                            if (reply.Status == IPStatus.Success)
                            {
                                complete();
                                return;
                            }
                        }
                        catch (Exception ex)
                        {
                            tellError(ex);
                            return;
                        }
                    }
                }
            }
            
            #endregion  
          
            #region Ping
		    else
            {
                for (int i = 0; i < 4; i++)
                {
                    if (!Stop)
                    {
                        try
                        {
                            PingReply reply = ping.Send(target, 1000, new byte[packetSize]);
                            count++;
                            sent++;
                            try
                            {
                                if (reply.Address != null)
                                {
                                    hn = getHostName(reply.Address.ToString());
                                }
                            }
                            catch (Exception ex)
                            {
                                hn = reply.Address.ToString();
                                tellError(ex);
                            }
                            tellUI(reply, hn); 
                            Thread.Sleep(250);
                        }
                        catch (Exception ex)
                        {
                            tellError(ex);
                            return;
                        }
                    }
                }
                complete();
            }
	        #endregion        
        }


        #region tellUI
        private delegate void tellUIDelegate(PingReply response, String hostname);
        public void tellUI(PingReply response, String hostname)
        {

            if (this.InvokeRequired)
            {
                this.BeginInvoke(new tellUIDelegate(tellUI), new object[] { response, hostname });
            }
            else
            {
                switch (response.Status)
                {
                    case (IPStatus.Success):
                        {

                            string address = response.Address.ToString();
                            string buffersize = response.Buffer.Length.ToString();
                            long triptime = response.RoundtripTime;
                            string status = response.Status.ToString();
                            string ttl = response.Options.Ttl.ToString();

                            calculateTripTimes(triptime);

                            results.AppendLine(String.Format("{0,4:###0}  Reply from {1,-15} [{2}], {3,4:###0} Bytes, in {4,4:###0}ms, TTL={5,4:###0}",
                                                                count, address, hostname, buffersize, triptime, ttl));

                            updateResults();
                            good++;
                            break;
                        }
                    case (IPStatus.TimedOut):
                        {
                            results.AppendLine(String.Format("{0}  Timeout!", count.ToString()));
                            bad++;
                            updateResults();
                            break;
                        }
                    case (IPStatus.DestinationHostUnreachable):
                        {
                            results.AppendLine(String.Format("{0}  Unreachable!", count.ToString()));
                            bad++;
                            updateResults();
                            break;
                        }
                    case (IPStatus.TtlExpired):
                        {
                            string address = response.Address.ToString();
                            string buffersize = response.Buffer.Length.ToString();
                            long triptime = response.RoundtripTime;
                            string status = response.Status.ToString();
                            //string ttl = response.Options.Ttl.ToString();

                            calculateTripTimes(triptime);

                            results.AppendLine(String.Format("{4}  Reply from {0}, {1} Bytes, in {2}ms, TTL={3}",
                                                                address, buffersize, triptime, count, count, status));

                            updateResults();
                            good++;
                            break;
                        }
                    default:
                        {
                            results.AppendLine(String.Format("{0}  Status Unknown!", count.ToString()));
                            bad++;
                            updateResults();
                            break;
                        }
                }
            }
        }
        
        #endregion

        #region tellError
        private delegate void tellErrorDelegate(Exception ex);
        public void tellError(Exception ex)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new tellErrorDelegate(tellError), new object[] { ex });
            }
            else
            {
                if(ex.GetType() == typeof(System.OverflowException))
                {                
                    results.AppendLine(String.Format("Error Message:  Packetsize too large"));
                }
                else
                {
                    results.AppendLine(String.Format("Error Message:  {0}", ex.Message));
                }
                updateResults();
            }
        }
        #endregion

        #region complete
        private delegate void completeDelegate();
        public void complete()
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new completeDelegate(complete), new object[] { });
            }
            else
            {
                if (!checkBoxTrace.Checked)
                {
                    results.AppendLine(String.Format("{1}Ping statistics for {0}:", target, Environment.NewLine));
                    results.AppendLine(String.Format("\tPackets: Sent={0}  Received={1}  Lost={2} ({3}%Lost)",
                                        sent.ToString(), good.ToString(), bad.ToString(), (((float)bad / sent) * 100).ToString()));
                    results.AppendLine(String.Format("Approximate round trip times in milliseconds:"));
                    results.AppendLine(String.Format("\tmin: {0}  max: {1}  avg: {2}",
                                        min.ToString(), max.ToString(), (avg / sent).ToString()));
                    updateResults();
                }
                else if (checkBoxTrace.Checked)
                {
                    results.AppendLine(String.Format("{1}Trace Complete for host: {0}:", target, Environment.NewLine));
                    results.AppendLine(String.Format("Approximate round trip times in milliseconds:"));
                    results.AppendLine(String.Format("\tmin: {0}  max: {1}  avg: {2}",
                                        min.ToString(), max.ToString(), (avg / sent).ToString()));
                    updateResults();
                }
                else
                {
                    //shouldn't be able to get here...
                }
            }
        } 
        #endregion

        #region traceResult
        private delegate void traceResultDelegate(string one, string two, string three, string host, bool error);
        public void traceResult(string one, string two, string three, string host, bool error)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new traceResultDelegate(traceResult), new object[] { one, two, three, host, error });
            }
            else
            {
                if (!error)
                {
                    results.AppendLine(String.Format("{0,4:###0} {1,4:###0}ms {2,4:###0}ms {3,4:###0}ms {4,-15} [{5}]",
                                                        count, one, two, three, host, getHostName(host)));
                    calculateTripTimes(Convert.ToInt32(one));
                    good++;
                    calculateTripTimes(Convert.ToInt32(two));
                    good++;
                    calculateTripTimes(Convert.ToInt32(three));
                    good++;
                    updateResults();
                }
                else if (error)
                {
                    results.AppendLine(String.Format("{0,4:###0} {1,4:###0}ms {2,4:###0}ms {3,4:###0}ms {4,-15} [{5}]",
                                                        count, one, two, three, host, getHostName(host)));

                    if (one == "*")
                    {
                        bad++;
                    }
                    else
                    {
                        calculateTripTimes(Convert.ToInt32(one));
                        good++;
                    }
                    if (two == "*")
                    {
                        bad++;
                    }
                    else
                    {
                        calculateTripTimes(Convert.ToInt32(two));
                        good++;
                    }
                    if (three == "*")
                    {
                        bad++;
                    }
                    else
                    {
                        calculateTripTimes(Convert.ToInt32(three));
                        good++;
                    }
                    updateResults();
                }
                else
                {
                    //shouldn't be able to get here...
                }

            }
        }
        
        #endregion
        
        #region calculateTripTimes
        public void calculateTripTimes(long triptime)
        {
            //min
            if (min == 0) { min = triptime; }
            else if (triptime < min) { min = triptime; }

            //max
            if (max == 0) { max = triptime; }
            else if (triptime > max) { max = triptime; }

            //avg
            avg += triptime;
        }
        #endregion

        private void buttonStop_Click(object sender, EventArgs e)
        {
            Stop = true;
        }

        private void resteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            reset();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void cutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Control ctrl = this.ActiveControl;
            if (ctrl != null)
            {
                if (ctrl is TextBox)
                {
                    TextBox tx = (TextBox)ctrl;
                    tx.Cut();
                }
            }
        }

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Control ctrl = this.ActiveControl;
            if (ctrl != null)
            {
                if (ctrl is TextBox)
                {
                    TextBox tx = (TextBox)ctrl;
                    tx.Copy();
                }
            }
        }

        private void pasteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Control ctrl = this.ActiveControl;
            if (ctrl != null)
            {
                if (ctrl is TextBox)
                {
                    TextBox tx = (TextBox)ctrl;
                    tx.Paste();
                }
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutPAEPing a = new AboutPAEPing();
            a.Show();
        }

        public void updateResults()
        {
            textBoxResults.Text = results.ToString();
            textBoxResults.SelectionStart = (textBoxResults.Text.Length - 1);
            textBoxResults.ScrollToCaret();
        }

        private void checkBoxTrace_CheckedChanged(object sender, EventArgs e)
        {
            if (checkboxContinuous.Checked)
            {
                checkboxContinuous.Checked = false;
            }
        }

        private void checkboxContinuous_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxTrace.Checked)
            {
                checkBoxTrace.Checked = false;
            }
        }

        public bool isIPv4Address(string host)
        {
            // this regex string matches a valid ip v4 address:
            string p11 = @"^((0|1[0-9]{0,2}|2[0-9]{0,1}|2[0-4][0-9]|25[0-5]|[3-9][0-9]{0,1})\.){3}(0|1[0-9]{0,2}|2[0-9]{0,1}|2[0-4][0-9]|25[0-5]|[3-9][0-9]{0,1})$";

            return Regex.IsMatch(host, p11);
        }

        public string getHostName(string host)
        {
            if (DNSLookup == true)
            {
                if (isIPv4Address(host))
                {
                    try
                    {
                        IPHostEntry hostResult = Dns.GetHostEntry(IPAddress.Parse(host));
                        return (hostResult.HostName.ToString());
                    }
                    catch
                    {
                        return ("Error looking up host");
                    }
                }
                return ("NO RESPONSE");
            }
            return (host);
        }

        private void toolStripStatusLabel1_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process process = new System.Diagnostics.Process();
            process.StartInfo.UseShellExecute = true;
            process.StartInfo.FileName = "http://paeping.codeplex.com/license";
            process.Start();

        }

        private void checkBoxDNSLookup_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxDNSLookup.Checked == true)
            {
                DNSLookup = true;
            }
            else
            {
                DNSLookup = false;
            }

        }
    //end of class
    }
    }
